//package com.tianji.promotion.service.impl;
//
//import com.tianji.common.exceptions.BadRequestException;
//import com.tianji.common.exceptions.BizIllegalException;
//import com.tianji.common.utils.StringUtils;
//import com.tianji.common.utils.UserContext;
//import com.tianji.promotion.domain.po.Coupon;
//import com.tianji.promotion.domain.po.ExchangeCode;
//import com.tianji.promotion.domain.po.UserCoupon;
//import com.tianji.promotion.enmus.CouponStatus;
//import com.tianji.promotion.enmus.ExchangeCodeStatus;
//import com.tianji.promotion.mapper.CouponMapper;
//import com.tianji.promotion.mapper.UserCouponMapper;
//import com.tianji.promotion.service.IExchangeCodeService;
//import com.tianji.promotion.service.IUserCouponService;
//import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
//import com.tianji.promotion.utils.*;
//import lombok.RequiredArgsConstructor;
//import org.redisson.api.RLock;
//import org.redisson.api.RedissonClient;
//import org.springframework.aop.framework.AopContext;
//import org.springframework.data.redis.core.StringRedisTemplate;
//import org.springframework.stereotype.Service;
//import org.springframework.transaction.annotation.Transactional;
//
//import java.time.LocalDateTime;
//import java.util.concurrent.TimeUnit;
//
///**
// * <p>
// * 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
// * </p>
// *
// * @author 晚风
// * @since 2023-09-29
// */
//@Service
//@RequiredArgsConstructor
//public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {
//
//    private final CouponMapper couponMapper;
//    private final IExchangeCodeService exchangeCodeService;
//    private final StringRedisTemplate redisTemplate;
//    private final RedissonClient redissonClient;
//
//    // 领取优惠券
//    @Override
////    @Transactional
//    public void receiveCoupon(Long id) {
//        // 1. 根据id查询优惠券信息  做相关校验
//        if (id == null) {
//            throw new BadRequestException("非法参数");
//        }
//        Coupon coupon = couponMapper.selectById(id);
//        if (coupon == null) {
//            throw new BadRequestException("优惠券不存在");
//        }
//        if (coupon.getStatus() != CouponStatus.ISSUING) {
//            throw new BadRequestException("该优惠券状态不是在发放");
//        }
//        LocalDateTime now = LocalDateTime.now();
//        if (now.isBefore(coupon.getIssueBeginTime()) || now.isAfter(coupon.getIssueEndTime())) {
//            throw new BadRequestException("优惠券已过期或未发放");
//        }
//        if (coupon.getTotalNum() <= 0 || coupon.getIssueNum() >= coupon.getTotalNum()) {
//            throw new BadRequestException("该优惠券库存不足");
//        }
//        Long userId = UserContext.getUser();
//        // 获取当前用户对该优惠券已领取数量
///*        Integer count = this.lambdaQuery().eq(UserCoupon::getUserId, userId).eq(UserCoupon::getCouponId, id).count();
//        if (count == null || count >= coupon.getUserLimit()){
//            throw new BadRequestException("以达到领取上限");
//        }
//        // 2. 优惠券的已发放数量 + 1
//        couponMapper.incrIssueNum(id);
//        // 3. 生成用户卷
//        saveUserCoupon(userId,coupon);*/
////        checkAndCreateUserCoupon(userId,coupon,null);
//
//        // intern  表示强制从常量池中取数据  这样可以保证 同一个用户只能拿到一个锁
////            synchronized (userId.toString().intern()){
////                checkAndCreateUserCoupon(userId, coupon, serialNum);
////            }
////        synchronized (userId.toString().intern()){
////            // 从aop中获取当前对象的代理对象
////            IUserCouponService userCouponServiceProxy = (IUserCouponService) AopContext.currentProxy();
////            userCouponServiceProxy.checkAndCreateUserCoupon(userId, coupon, null); // 使用代理对象调用方法  方法是有事务处理的
////        }
//        // 通过工具类实现分布式锁
////        String key = "lock:coupon:uid:" + userId;
////        RedisLock isLock = new RedisLock(key, redisTemplate);
////        try {
////            boolean lockBool = isLock.tryLock(5, TimeUnit.SECONDS);
////            if (!lockBool) {
////                throw new BizIllegalException("操作太频繁了");
////            }
////            // 从aop中获取当前对象的代理对象
////            IUserCouponService userCouponServiceProxy = (IUserCouponService) AopContext.currentProxy();
////            userCouponServiceProxy.checkAndCreateUserCoupon(userId, coupon, null); // 使用代理对象调用方法  方法是有事务处理的
////        } finally {
////            isLock.unlock();
////        }
//
//        // 通过redisson实现分布式锁
////        String key = "lock:coupon:uid:" + userId;
//        // 从aop中获取当前对象的代理对象
//        IUserCouponService userCouponServiceProxy = (IUserCouponService) AopContext.currentProxy();
//        userCouponServiceProxy.checkAndCreateUserCoupon(userId, coupon, null); // 使用代理对象调用方法  方法是有事务处理的
//    }
//
//    @Override
//    @Transactional
//    public void exchangeCoupon(String code) {
//        // 1. 判断code是否为空
//        if (StringUtils.isBlank(code)) {
//            throw new BadRequestException("非法参数");
//        }
//        // 2. 解析兑换码获取自增id
//        Long serialNum = CodeUtil.parseCode(code);
//        // 3. 判断是否已兑换 使用redis的bitmap结构 setbit key offset 1 如果方法返回true表示兑换码已兑换
//        boolean result = exchangeCodeService.updateExchangeCodeMark(serialNum, true);
//        if (result) {
//            // 表示兑换码已被兑换
//            throw new BizIllegalException("兑换码已被兑换");
//        }
//        try {
//            // 4. 判断是否存在
//            ExchangeCode exchangeCode = exchangeCodeService.getById(serialNum);
//            if (exchangeCode == null) {
//                throw new BizIllegalException("兑换码不存在");
//            }
//            // 5. 判断兑换码是否过期
//            LocalDateTime now = LocalDateTime.now();
//            LocalDateTime expiredTime = exchangeCode.getExpiredTime();
//            if (now.isAfter(expiredTime)) {
//                throw new BizIllegalException("兑换码已过期");
//            }
//            Coupon coupon = couponMapper.selectById(exchangeCode.getExchangeTargetId());
//            Long userId = UserContext.getUser();
//            if (coupon == null) {
//                throw new BizIllegalException("优惠券不存在");
//            }
//            checkAndCreateUserCoupon(userId, coupon, serialNum);
//        } catch (Exception e) {
//            // 将兑换码状态重置
//            exchangeCodeService.updateExchangeCodeMark(serialNum, false);
//            throw e;
//        }
//
//    }
//
//    @MyLock(name = "lock:coupon:uid:#{userId}", lockType = MyLockType.RE_ENTRANT_LOCK, lockStrategy = MyLockStrategy.FAIL_AFTER_RETRY_TIMEOUT)
//    @Transactional
//    public void checkAndCreateUserCoupon(Long userId, Coupon coupon, Long serialNum) {
//        synchronized (userId.toString().intern()) {
//            Integer count = this.lambdaQuery()
//                    .eq(UserCoupon::getUserId, userId)
//                    .eq(UserCoupon::getCouponId, coupon.getId()).count();
//            if (count == null || count >= coupon.getUserLimit()) {
//                throw new BadRequestException("以达到领取上限");
//            }
//            // 2. 优惠券的已发放数量 + 1
//            couponMapper.incrIssueNum(coupon.getId());
//            // 3. 生成用户卷
//            saveUserCoupon(userId, coupon);
//            // 4. 更新兑换码的状态
//            if (serialNum != null) {
//                exchangeCodeService.lambdaUpdate()
//                        .set(ExchangeCode::getUserId, userId)
//                        .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED)
//                        .eq(ExchangeCode::getId, serialNum)
//                        .update();
//            }
//        }
//    }
//
//    // 保存用户卷
//    private void saveUserCoupon(Long userId, Coupon coupon) {
//        UserCoupon userCoupon = new UserCoupon();
//        userCoupon.setUserId(userId);
//        userCoupon.setCouponId(coupon.getId());
//        LocalDateTime issueBeginTime = coupon.getIssueBeginTime(); // 优惠券使用开始时间
//        LocalDateTime issueEndTime = coupon.getIssueEndTime();  // 优惠券使用截止时间
//        if (issueBeginTime == null && issueEndTime == null) {  // 判断是否有传时间 如果没有传 就把当前时间给到开始时间  把有效期的天数 加到结束时间
//            issueBeginTime = LocalDateTime.now();
//            issueEndTime = issueBeginTime.plusDays(coupon.getTermDays());
//        }
//        coupon.setIssueBeginTime(issueBeginTime);
//        coupon.setIssueEndTime(issueEndTime);
//        this.save(userCoupon);
//    }
//}
